package net.avcompris.binding.impl;

import static com.avcompris.util.ExceptionUtils.nonNullArgument;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;

import com.avcompris.common.annotation.Nullable;
import com.avcompris.lang.NotImplementedException;
import com.avcompris.util.AbstractUtils;

public abstract class TypeUtils extends AbstractUtils {

	public static Object findEnumConstant(final Class<?> enumClass,
			@Nullable final String value) {

		nonNullArgument(enumClass, "enumClass");

		if (value == null) {
			return null;
		}

		final Method nameMethod;

		try {

			nameMethod = enumClass.getMethod("name");

		} catch (final NoSuchMethodException e) {
			throw new IllegalStateException(
					"Cannot find name() method in Enum class: "
							+ enumClass.getName());
		}

		for (final Object constant : enumClass.getEnumConstants()) {

			final Object name;

			try {

				name = nameMethod.invoke(constant);

			} catch (final IllegalArgumentException e) {
				throw new RuntimeException(e);
			} catch (IllegalAccessException e) {
				throw new RuntimeException(e);
			} catch (InvocationTargetException e) {
				throw new RuntimeException(e);
			}

			if (value.equals(name) || value.equals(constant.toString())) {

				return constant;
			}
		}

		throw new IllegalArgumentException("Cannot find Enum value for: "
				+ value + " in class: " + enumClass.getName());

	}

	public static <U> U unmarshallValue(final Class<U> type, final String text) {

		nonNullArgument(type, "type");
		nonNullArgument(text, "text");

		if (type.isArray()) {

			throw new IllegalStateException("Type is array: " + type.getName());
		}

		final Object value;

		if (String.class.equals(type)) {

			value = text;

		} else if (Character.class.equals(type)) {

			if (text.length() != 1) {
				throw new IllegalArgumentException(
						"Text value to cast to Character should be of length 1: "
								+ text.length() + ": " + text);
			}

			value = Character.valueOf(text.charAt(0));

		} else if (Integer.class.equals(type)) {

			try {

				value = Integer.valueOf(text);

			} catch (final NumberFormatException e) {
				throw new RuntimeException(
						"Text value to cast to Integer is invalid: " + text);
			}

		} else if (type.isEnum()) {

			value = TypeUtils.findEnumConstant(type, text);

		} else if (DateTime.class.equals(type)) {

			final String pattern;

			if (text.contains("T") && text.contains(".")) {
				pattern = "YYYY-MM-dd'T'HH:mm:ss.SSSZ";
			} else if (text.contains("T")) {
				pattern = "YYYY-MM-dd'T'HH:mm:ssZ";
			} else {
				pattern = "YYYY-MM-dd HH:mm:ss";
			}

			value = DateTimeFormat.forPattern(pattern).parseDateTime(text);

		} else {

			throw new NotImplementedException(text + ": " + type);
		}

		return type.cast(value);
	}
}
